package pl.shenlon.poker.game.gametypes;

import java.util.Observable;

import pl.shenlon.poker.cards.Card;
import pl.shenlon.poker.cards.Hand;
import pl.shenlon.poker.cards.deck.Deck;
import pl.shenlon.poker.cards.deck.PokerDeck;
import pl.shenlon.poker.cards.evaluation.HandRank;
import pl.shenlon.poker.cards.evaluation.HandRankEvaluator;
import pl.shenlon.poker.game.PokerPlayer;

public abstract class PokerGame extends Observable {

	public static final int MAX_RAISE_COUNT = 4;

	protected final Deck deck;

	protected HandRankEvaluator handRankEvaluator;

	protected PokerPlayer playerA, playerB;

	protected int playerATotalBets = 0, playerBTotalBets = 0;

	protected int pot, betSize;

	protected int raiseCounter;

	protected int smallBlind, bigBlind;

	protected Hand tableCards;

	protected PokerPlayer winner;

	private PokerGame() {
		deck = new PokerDeck();
		smallBlind = 10;
		bigBlind = 20;
		raiseCounter = 0;
		pot = 0;
		handRankEvaluator = HandRankEvaluator.getInstance();
	}

	public PokerGame(int tableSize) {
		this();
		tableCards = new Hand(tableSize);
	}

	public void clearPlayersBets() {
		playerATotalBets = 0;
		playerBTotalBets = 0;
	}

	public void clearWinner() {
		winner = null;
	}

	public void dealCards() {
		for (Card card : deck.getCardsInDeck()) {
			card.setFaceUp(false);
		}
		deck.shuffle();
		for (int i = 0; i < playerA.getHand().getCardsCount(); i++) {
			playerA.getHand().setCard(i, deck.next());
			playerB.getHand().setCard(i, deck.next());
		}
		for (int i = 0; i < tableCards.getCardsCount(); i++) {
			tableCards.setCard(i, deck.next());
		}
	}

	public int getBetSize() {
		return betSize;
	}

	public int getBigBlind() {
		return bigBlind;
	}

	public PokerPlayer getPlayerA() {
		return playerA;
	}

	public int getPlayerATotalBets() {
		return playerATotalBets;
	}

	public PokerPlayer getPlayerB() {
		return playerB;
	}

	public int getPlayerBTotalBets() {
		return playerBTotalBets;
	}

	public int getPot() {
		return pot;
	}

	public int getRaiseCounter() {
		return raiseCounter;
	}

	public int getSmallBlind() {
		return smallBlind;
	}

	public Hand getTableCards() {
		return tableCards;
	}

	public PokerPlayer getWinner() {
		return winner;
	}

	public void hideTableCards() {
		for (Card card : tableCards.getCards()) {
			card.setFaceUp(false);
		}
	}

	public abstract int nextBetAmount();

	public PokerPlayer playGame(boolean dealCards) {
		winner = null;
		pot = 0;
		if (playerA.canBet(smallBlind)) {
			pot = smallBlind;
			playerA.addCash(-smallBlind);
			playerATotalBets += smallBlind;
		} else {
			return playerB;
		}

		if (playerB.canBet(bigBlind)) {
			pot += bigBlind;
			playerB.addCash(-bigBlind);
			playerBTotalBets += bigBlind;
		} else {
			return playerA;
		}

		if (dealCards) {
			dealCards();
		}
		turnPreFlop();
		if (winner != null) {
			winner.addCash(pot);
			return winner;
		}

		turnFlop();
		if (winner != null) {
			winner.addCash(pot);
			return winner;
		}

		turnTurn();
		if (winner != null) {
			winner.addCash(pot);
			return winner;
		}

		turnRiver();
		if (winner != null) {
			winner.addCash(pot);
			return winner;
		}

		HandRank playerAHandRank = handRankEvaluator.evaluateHand(
				playerA.getHand(), tableCards), playerBHandRank = handRankEvaluator
				.evaluateHand(playerB.getHand(), tableCards);
		int comparisionResult = playerAHandRank.compareTo(playerBHandRank);
		if (comparisionResult > 0) {
			playerA.addCash(pot);
			return playerA;
		} else if (comparisionResult < 0) {
			playerB.addCash(pot);
			return playerB;
		}
		playerA.addCash(pot / 2);
		playerB.addCash(pot / 2);
		return playerA;
	}

	public void setBetSize(int betSize) {
		this.betSize = betSize;
	}

	public void setBigBlind(int bigBlind) {
		this.bigBlind = bigBlind;
	}

	public void setPlayerA(PokerPlayer playerA) {
		this.playerA = playerA;
	}

	public void setPlayerB(PokerPlayer playerB) {
		this.playerB = playerB;
	}

	public void setPot(int pot) {
		this.pot = pot;
	}

	public void setSmallBlind(int smallBlind) {
		this.smallBlind = smallBlind;
	}

	public abstract void turnFlop();

	public abstract void turnPreFlop();

	public abstract void turnRiver();

	public abstract void turnTurn();
}